Skip to content

Fix browser webview divider resizing - #7038

Merged
austinywang merged 8 commits into
mainfrom
issue-7037-webview-divider-resize
Jun 29, 2026
Merged

austinywang merged 8 commits into
mainfrom
issue-7037-webview-divider-resize

Conversation

@austinywang

@austinywang austinywang commented Jun 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #7037

Browser panes now decline hits over real adjacent split-divider grab bands before descending into the hosted WKWebView. This prevents WebKit's internal scrollbar view from winning the mouse-down when the pointer is in the pane divider band, while preserving normal webview clicks, scrolling, selection, and DevTools inspector divider hits outside that band.

I used the HostContainerView.hitTest pass-through alternative instead of adding a child overlay in pinHostedWebView(): a child overlay would become the hit view and then has to rely on responder-chain forwarding to reach the split view, while returning nil from the host over a verified external divider band routes directly through the existing bonsplit/SwiftUI resize paths. The webview frame stays unchanged.

Verification

  • Confirmed the root cause in code: the webview is inserted above the slot and pinned to fill its container, while bonsplit expands the 1px divider by 5pt.
  • git diff --check
  • No local tests or local build run per task instruction.

Test note

I did not add a regression test because the failure depends on WKWebView's private internal scrollbar view winning AppKit hit-testing while layered over an NSSplitView divider. A unit test of the helper math would not prove the user-visible regression, and a meaningful test would need an end-to-end UI/AppKit/WebKit hit-test harness.

Localization

No user-facing strings were added or changed. The only added string literal is a DEBUG hit-test log stage.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes divider resizing and cursor/hover behavior in browser panes (including the docked right sidebar) by centralizing divider hit geometry and passing near-edge divider hits through WKWebView to the parent NSSplitView. Also updates Swift file length budgets.

  • Bug Fixes
    • Share a 5pt divider hit band and window hit rects via PortalSplitDividerRegion (dividerHitExpansion, hitRectInWindow, dividerHitRect{,InWindow}) used across browser, terminal, and panel hit-testing.
    • Add a near-edge prefilter and only pass through when a real external split divider is hit; preserves normal web actions and ignores hosted inspector divider hits.
    • Use the Dock browser edge when present to detect trailing sidebar resize hits, falling back to the content edge otherwise.
    • Improve cursor feedback: when pass-through applies, clear the webview divider cursor and compute cursor kind via shared hitRectInWindow in both portals.

Written for commit c3ea0b4. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Fixed browser split-pane edge pointer routing so resize/drag interactions correctly pass through to the external divider when appropriate, with matching cursor behavior.
    • Standardized divider hit-testing across browser and terminal views for more consistent hover and resizing detection.
    • Improved interactive divider drag detection accuracy using shared hit-rectangle calculations.
    • Corrected pass-through diagnostics in debug builds.

@vercel

vercel Bot commented Jun 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 29, 2026 7:08pm
cmux-staging Building Building Preview, Comment Jun 29, 2026 7:08pm

@coderabbitai

coderabbitai Bot commented Jun 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: db30f7b6-7eec-4883-8489-977c8a35cd3f

📥 Commits

Reviewing files that changed from the base of the PR and between f87f8e6 and c3ea0b4.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (1)
  • Sources/BrowserWindowPortal.swift

📝 Walkthrough

Walkthrough

Centralizes split-divider hit geometry, updates portal cursor and drag detection to use shared hit rectangles, and adds browser panel hit-test pass-through so nearby external split dividers can receive resize events.

Changes

Split divider hit testing

Layer / File(s) Summary
Shared divider geometry
Sources/PortalSplitDividerRegion.swift
Adds shared divider rect and hit-rect helpers, updates region hit expansion, and switches divider-region construction to the shared divider rect helper.
Portal hit consumers
Sources/BrowserWindowPortal.swift, Sources/TerminalWindowPortal.swift
Switches divider, drag, and cursor detection to the shared divider hit rectangles, and marks right-sidebar dock slots for trailing sidebar hit-testing.
Browser panel pass-through
Sources/Panels/BrowserPanelView.swift
Adds external split-divider pass-through detection in hitTest(_:), including ancestor split-view traversal and divider hit-rect checks, and fixes the debug log guard scope.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#6592: Both PRs adjust split-divider pointer hit-testing and routing using shared portal divider geometry.

Suggested reviewers

  • Ari4ka

Poem

🐇 I hopped to the split and peered at the seam,
The divider found room in the hit-test stream.
Webviews stepped aside, the resize path shone,
And the pane could be dragged right home.


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swift File And Package Boundaries ❌ Error HEAD adds/expands huge app-target Swift files (8k/4k/2k lines) and a 242-line budget TSV bump, matching the rule’s explicit failure cases. Split reusable hit-testing/geometry into smaller files or a SwiftPM package, keep only thin AppKit glue in Sources/, and remove the budget-table growth.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (23 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: fixing browser webview divider resizing.
Description check ✅ Passed The description includes the key summary and verification details, with only some template sections like demo video, review trigger, and checklist omitted.
Linked Issues check ✅ Passed The changes address both the main-pane and Dock divider hit-testing cases while preserving inspector divider behavior.
Out of Scope Changes check ✅ Passed The diff stays focused on divider geometry, hit-testing, and cursor routing with no unrelated feature work.
Cmux Swift Actor Isolation ✅ Passed Changes stay in AppKit/UI types (NSView/@mainactor) and add no new Sendable, protocol, or background-accessed state isolation problems.
Cmux Swift Blocking Runtime ✅ Passed Changed Swift code only adds hit-test/cursor geometry and timestamp checks; no semaphores, sleeps, sync waits, locks, or polling appear in modified paths.
Cmux Browser Automation Off-Main ✅ Passed PASS: The PR only changes browser pane hit-testing/divider geometry in UI files; it doesn’t touch TerminalController.swift or the socket-worker policy/routing code the rule governs.
Cmux Expensive Synchronous Load ✅ Passed The diff only changes split-divider hit-testing/cursor geometry; no agent-history/session loads, JSON/JSONL parsing, or main-actor load paths were added.
Cmux Cache Substitution Correctness ✅ Passed Changes only adjust transient hit-testing/cursor geometry; any cached divider regions are freshness-checked and invalidated, not used as persisted history/snapshot data.
Cmux No Hacky Sleeps ✅ Passed The PR only changes Swift and a TSV budget file; the no-hacky-sleeps rule is out of scope, and no non-Swift runtime sleeps/timers were added.
Cmux Algorithmic Complexity ✅ Passed New scans are limited to tiny split-view/slot lists in hot UI hit-testing; no large-data batch rescans or datastore joins were introduced.
Cmux Swift Concurrency ✅ Passed Diff adds only synchronous AppKit hit-testing/cursor geometry helpers; no new DispatchQueue, Combine, or unmanaged Task patterns were introduced.
Cmux Swift @Concurrent ✅ Passed Touched Swift changes are synchronous/AppKit-bound; no @concurrent misuse, nonisolated async gap, or UI-heavy helper was introduced.
Cmux Swiftpm Lockfiles ✅ Passed The change set is limited to Swift source files; no Package.swift, Package.resolved, xcodeproj package-ref, or .gitignore rules ignoring Package.resolved were changed.
Cmux Swift Logging ✅ Passed New BrowserPanelView hit-test logs are wrapped in #if DEBUG and use cmuxDebugLog; no print/debugPrint/dump/NSLog or Logger constants appear in touched files.
Cmux User-Facing Error Privacy ✅ Passed Diff only changes hit-testing/cursor geometry and DEBUG logging; no user-facing errors, alerts, command output, or recovery copy were added.
Cmux Full Internationalization ✅ Passed Touched Swift changes only alter hit-testing/cursor geometry and DEBUG logs; no new user-facing text or locale assets were added.
Cmux Swiftui State Layout ✅ Passed Diff only changes AppKit bridge hit-testing/cursor geometry; no new @ObservableObject/lazy-row/store patterns or render-time state writes were introduced.
Cmux Architecture Rethink ✅ Passed Local hit-testing/cursor fix uses shared divider geometry helpers and a single slot flag; no sleeps, delayed dispatch, or split lifecycle ownership were introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed Touched files only adjust NSView hit-testing/divider geometry; no new NSWindow/NSPanel/WindowGroup or cmuxAuxiliaryWindowIdentifiers changes.
Cmux Source Artifacts ✅ Passed Changed paths are source/config/review-rule files; no temp, cache, build-output, DerivedData, screenshot, or scratch dirs appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The PR adds divider-hit geometry and existing DEBUG logging only; no new test/debug observability seam or wrapper accessor appears in production Sources.
Cmux No Ambient Global State ✅ Passed Added logic stays inside existing types as private methods/fileprivate state; only a static let constant and helpers, with no new file-scope mutable vars or singletons.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-7037-webview-divider-resize

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Panels/BrowserPanelView.swift`:
- Around line 6684-6724: `externalSplitDividerHit` currently duplicates the
divider geometry used by `PortalSplitDividerRegion.appendDividerRegions`, and
the hit expansion is split across `resetCursorRects`, `dividerCursorKind`, and
this helper. Update the hit-test path to derive the divider region from
`PortalSplitDividerRegion` (or shared geometry logic) and centralize the
expansion in one constant so `externalSplitDividerHit`, `resetCursorRects`, and
`dividerCursorKind` all use the same values and stay in sync.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: df43144d-2310-4106-846b-e4948b9934af

📥 Commits

Reviewing files that changed from the base of the PR and between 19ddb2e and 474e7ad.

📒 Files selected for processing (1)
  • Sources/Panels/BrowserPanelView.swift

Comment thread Sources/Panels/BrowserPanelView.swift
@greptile-apps

greptile-apps Bot commented Jun 29, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes browser pane divider resizing by overriding HostContainerView.hitTest to return nil when the pointer is inside a verified external NSSplitView divider band, preventing the hosted WKWebView's internal scrollbar view from winning mouse-down. It also centralizes divider hit-band geometry in PortalSplitDividerRegion static helpers and corrects the trailing sidebar dock-slot detection when computing the content trailing edge.

  • PortalSplitDividerRegion gains three static helpers (dividerRect, dividerHitRect, dividerHitRectInWindow) and a hitRectInWindow computed property, unifying the 5-pt expansion constant and removing duplicated inline geometry.
  • HostContainerView adds shouldPassThroughToExternalSplitDivider, guarded by a near-edge pre-filter and window-coordinate hit rect confirmation; updateDividerCursor mirrors this with restoreArrow: false.
  • WindowBrowserSlotView gains isRightSidebarDockSlot so the trailing-edge heuristic uses frame.minX of a dock slot rather than frame.maxX, fixing docked right-sidebar resize.

Confidence Score: 5/5

Safe to merge. The hit-test pass-through is tightly guarded by a near-edge pre-filter and an explicit window-coordinate divider-rect confirmation, preserving normal webview clicks and scrolling.

All changed paths are AppKit pointer-routing helpers on the main actor with no actor-isolation or concurrency concerns. The shared geometry helpers in PortalSplitDividerRegion are straightforward NSRect math with correct bounds-clamping. The dock-slot flag is scoped to the slot view and set at the same call site as its backing drop context. The cursor and hit-test pass-through logic are symmetric.

No files require special attention.

Important Files Changed

Filename Overview
Sources/PortalSplitDividerRegion.swift Clean extraction of three static geometry helpers and a hitRectInWindow computed property, centralising the 5-pt expansion constant and bounds-intersection logic previously duplicated in three callers.
Sources/Panels/BrowserPanelView.swift Adds shouldPassThroughToExternalSplitDivider with near-edge pre-filter and window-coordinate confirmation; updateDividerCursor correctly uses restoreArrow: false so the split view resize cursor takes over.
Sources/BrowserWindowPortal.swift Replaces duplicated scroll-monitor geometry with shared dividerHitRect helper; adds isRightSidebarDockSlot flag to correctly distinguish dock-slot minX from content-slot maxX in the trailing resizer heuristic.
Sources/TerminalWindowPortal.swift Trivial refactor replacing inline expansion math with the new hitRectInWindow property; no behavior change.
.github/swift-file-length-budget.tsv Budget updates consistent with net line changes across all three modified Swift files.

Reviews (3): Last reviewed commit: "Use Dock browser edge for sidebar resize..." | Re-trigger Greptile

Comment on lines +6698 to +6707
private func isNearPaneEdge(_ point: NSPoint) -> Bool {
let expansion = PortalSplitDividerRegion.dividerHitExpansion
let nearVerticalEdge = point.x >= bounds.minX &&
point.x <= bounds.maxX &&
(point.x <= bounds.minX + expansion || point.x >= bounds.maxX - expansion)
let nearHorizontalEdge = point.y >= bounds.minY &&
point.y <= bounds.maxY &&
(point.y <= bounds.minY + expansion || point.y >= bounds.maxY - expansion)
return nearVerticalEdge || nearHorizontalEdge
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 isNearPaneEdge compares hitTest point in superview coordinates against bounds in local coordinates

NSView.hitTest(_:) receives point in the superview's coordinate system, but bounds.minX/bounds.maxX/bounds.minY/bounds.maxY are in the view's own coordinate system. The check works today because HostContainerView is always pinned to origin (0,0) inside its container (so superview coordinates coincide with local coordinates), but the assumption is invisible. If the view is ever laid out with a non-zero frame origin the near-edge predicate would silently misfire, causing either spurious pass-throughs or missed ones.

Consider converting the point to local coordinates first (let local = superview?.convert(point, to: self) ?? point) and comparing against bounds, or adding a comment that documents the origin-is-zero invariant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in f87f8e6: now documents and guards the local-bounds coordinate invariant, and mirrors the external split-divider pass-through path with so the underlying split cursor can take over.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to the previous reply: addressed in f87f8e6. isNearPaneEdge now documents and guards the local-bounds coordinate invariant, and updateDividerCursor mirrors the external split-divider pass-through path with restoreArrow false so the underlying split cursor can take over.

— Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/BrowserWindowPortal.swift`:
- Around line 821-822: In BrowserWindowPortal’s divider-hit logic, the current
contentSlots/maxX path can miss the Dock divider when the Dock is the only
browser pane or when the adjacent area is a terminal. Update the divider
coordinate lookup to prefer the marked dock slot’s frame.minX as the primary
source, then fall back to the existing non-dock content edge computation. Make
sure the dock-divider hit test in the same routing path fails closed when
neither source is available, rather than letting the WebView consume the
mouse-down.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c5e58930-ec89-4911-a727-8f94f344b924

📥 Commits

Reviewing files that changed from the base of the PR and between 807e40c and 55c78ab.

⛔ Files ignored due to path filters (1)
  • .github/swift-file-length-budget.tsv is excluded by !**/*.tsv
📒 Files selected for processing (1)
  • Sources/BrowserWindowPortal.swift

Comment thread Sources/BrowserWindowPortal.swift Outdated
@austinywang
austinywang merged commit e4480db into main Jun 29, 2026
50 of 52 checks passed

This branch was successfully deployed

1 active deployment
Preview – cmux — c3ea0b43 Deployed Jun 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't resize pane/Dock divider when the adjacent pane is a browser webview (scrollbar swallows the drag)

1 participant